home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_100 / 181_01 / where.c < prev    next >
Encoding:
C/C++ Source or Header  |  1985-12-11  |  38.5 KB  |  776 lines

  1. /****************************************************************************
  2. *                                WHERE                                      *
  3. *                                                                           *
  4. *  WHERE is a program to locate files on a disk.  It requires DOS 2.x or    *
  5. *  above.  It will search through all subdirectories of the disk looking    *
  6. *  for files which match information coming from the command line.          *
  7. *                                                                           *
  8. *  The command line syntax is:                                              *
  9. *                                                                           *
  10. *     WHERE [ starting directory ] filename.ext [p] (for file prompting)    *
  11. *                                                                           *
  12. *     Rule: if a directory name is the last item in the path name, then     *
  13. *           the directory name MUST be followed by a backslash or else it   *
  14. *           will be considered a filespec.                                  *
  15. *                                                                           *
  16. *  This program originally came from the Oct. 85 issue of PC Tech Journal.  *
  17. *  While it was written originally for the Mark Williams compiler, this     *
  18. *  version is for the Microsoft C compiler, vsn 3.0 or later.               *
  19. *                                                                           *
  20. *  George Defenbaugh,  918-622-7926,  10034 E. 29th St.  Tulsa, OK  74129   *
  21. ****************************************************************************/
  22.  
  23. /****************************************************************************
  24. *  C header files                                                           *
  25. *  These identify library routines.  LINT_ARGS must be defined first.       *
  26. ****************************************************************************/
  27.  
  28. #define LINT_ARGS = 1      /* value doesn't matter, only definition does   */
  29.  
  30. #include <conio.h>         /* for console i/o                              */
  31. #include <dos.h>           /* for DOS interrupt calls                      */
  32. #include <stdio.h>         /* for standard i/o                             */
  33. #include <string.h>        /* for string functions                         */
  34.  
  35. union REGS inregs,outregs; /* register structure for DOS calls             */
  36.  
  37. /****************************************************************************
  38. *  Structure for MS-DOS date and time fields returned by DOS functions      *
  39. *  See DOS Tech Reference for more information                              *
  40. ****************************************************************************/
  41.  
  42. struct msdos_date
  43. {
  44.    unsigned ms_sec      : 5; /* time in 2 sec. int (5 bits)             */
  45.    unsigned ms_min      : 6; /* minutes (6 bits)                        */
  46.    unsigned ms_hour     : 5; /* hours (5 bits)                          */
  47.    unsigned ms_day      : 5; /* day of the month (5 bits)               */
  48.    unsigned ms_month    : 4; /* month (4 bits)                          */
  49.    unsigned ms_year     : 7; /* year since 1980 (7 bits)                */
  50. };
  51.  
  52. /****************************************************************************
  53. *  Define the DOS Disk Transfer Area (DTA).  The structure will be filled   *
  54. *  in by DOS for interrupt 21H calls.  See DOS Tech Ref for more info       *
  55. ****************************************************************************/
  56.  
  57. struct DTA
  58. {
  59.    char     DTA_dosinfo[21];      /* reserved for DOS use               */
  60.    char     DTA_attr;             /* file attribute byte                */
  61.    struct   msdos_date DTA_date;  /* date structure from above          */
  62.    long     DTA_size;             /* file size                          */
  63.    char     DTA_filename[13];     /* file name (w/o path)               */
  64. };
  65.  
  66. /****************************************************************************
  67. *  Macros                                                                   *
  68. ****************************************************************************/
  69.  
  70. /* This macro returns the number of elements of its array argument         */
  71.  
  72. #define SIZE(x)     (sizeof(x)/sizeof(x[0]))
  73.  
  74. /****************************************************************************
  75. *  Definition of constants                                                  *
  76. ****************************************************************************/
  77.  
  78. #define not !                 /* C normally uses the ! for NOT             */
  79. #define and &                 /* C normally uses the & for AND             */
  80. #define lower_half_reg 0x00ff /* mask for anding into lower register half  */
  81. #define carry_set      0x0001 /* mask for flag register carry bit          */
  82. #define no_type        0x06   /* gets system and hidden files too          */
  83. #define directory_type 0x16   /* directory bit on, with system and hidden  */
  84. #define directory_only 0x10   /* directory bit only is on                  */
  85. #define no_more_files  18     /* DOS return code for no more files         */
  86. #define full_screen    23     /* max number of lines on a screen           */
  87. #define end_of_string  '\0'   /* C uses a binary zero for end of string    */
  88. #define backslash      '\\'   /* The backslash character                   */
  89. #define colon          '\:'   /* The colon character                       */
  90. #define stop           'n'    /* Used with the continue_pgm switch         */
  91. #define no             'n'    /* Used with the user_want_prompt switch     */
  92. #define continue       'y'    /* Used with the continue_pgm switch         */
  93. #define yes            'y'    /* Used with the user_want_prompt switch     */
  94. #define a_ronly        0x01   /* Read only file                            */
  95. #define a_hidden       0x02   /* Hidden file                               */
  96. #define a_system       0x04   /* System file                               */
  97. #define a_directory    0x10   /* Directory                                 */
  98. #define a_archive      0x20   /* Archive bit                               */
  99. #define n_directory    'd'    /* switch value if directories are prompted  */
  100. #define n_ronly        'r'    /* Read only file                            */
  101. #define n_write        'w'    /* Write allowed                             */
  102. #define n_hidden       'h'    /* Hidden file                               */
  103. #define n_visible      'v'    /* Visible file                              */
  104. #define n_system       's'    /* System file                               */
  105. #define n_user         'u'    /* User file                                 */
  106. #define n_archive      'a'    /* Archive bit                               */
  107. #define n_not_archive  'n'    /* Not archived                              */
  108.  
  109. /****************************************************************************
  110. *  The next structure and array are for analyzing the attribute bits for    *
  111. *     each file.  The structure consists of pairs of integers, the first    *
  112. *     integer in each pair is a bit mask which has the bit set that         *
  113. *     corresponds to a particular attribute for a file.  The second integer *
  114. *     is a character that is displayed if the attribute byte has the bit    *
  115. *     set that matches the mask.  The attribute bits are:                   *
  116. *                                                                           *
  117. *           a - archive bit                                                 *
  118. *           d - directory                                                   *
  119. *           h - hidden file                                                 *
  120. *           r - read only file                                              *
  121. *           s - system file                                                 *
  122. ****************************************************************************/
  123.  
  124. static  struct atr {          /* Attribute structure                       */
  125.                     char a_mask;
  126.                     char a_name;
  127.                    } 
  128. atr[] = {
  129.          a_archive,   n_archive,
  130.          a_directory, n_directory,
  131.          a_system,    n_system,
  132.          a_hidden,    n_hidden,
  133.          a_ronly,     n_ronly
  134.         };
  135.  
  136. char *time_of_day[2] = {"AM","PM"};
  137.  
  138. /****************************************************************************
  139. *  Define the type "filename" to be a string of 51 characters               *
  140. *    This type is used in other definitions                                 *
  141. ****************************************************************************/
  142.  
  143. typedef char filename[51];
  144.  
  145. /****************************************************************************
  146. *  The following filename strings are used in the program                   *
  147. *                                                                           *
  148. *     check_string               filename to be searched for from the       *
  149. *                                   command line                            *
  150. *     directory_string           directory name to be searched              *
  151. *     newdirectory_string        directory name to be searched on next      *
  152. *                                   recursive call                          *
  153. *     current_string             temporary string for searching in a        *
  154. *                                specific directory                         *
  155. *                                                                           *
  156. ****************************************************************************/
  157.  
  158. /****************************************************************************
  159. *  Definition of any forward-referencing functions                          *
  160. ****************************************************************************/
  161.  
  162. char *DATE();
  163.  
  164. /****************************************************************************
  165. *  Global variables                                                         *
  166. ****************************************************************************/
  167.  
  168. filename check_string;        /* this string "remembers" user input        */
  169. char     datestring[40];      /* print output string for dates             */
  170. char     continue_pgm = continue;  /* switch to stop program               */
  171. char     continue_dir = yes;       /* continue this directory switch       */
  172. char     user_want_prompt = no;    /* switch to allow deletion of files    */
  173. int      nbr_lines_on_screen = 0; /* counts nbr lines already printed      */
  174. char     attr_mask_switch = 0x00; /* for selection by attribute            */
  175. char     *first_switch;           /* addr of first command line switch     */
  176. char     *first_switch_copy;      /* copy first command line switch addr   */
  177.  
  178. /****************************************************************************
  179. *  MAIN() - Beginning of the code                                           *
  180. ****************************************************************************/
  181.  
  182. main(argc,argv)
  183.  
  184. int argc;
  185. char *argv[];                     /* could also be: char **argv;           */
  186.  
  187. {
  188.    filename  directory_string;    /* directory to be searched              */
  189.    char      *incoming_filename;  /* addr of filename in command line arg  */
  190.    char      *last_backslash;     /* addr of last backslash in cmd line    */
  191.    char      *last_colon;         /* addr of last colon command line       */
  192.    char      *incoming_string;    /* addr of first command line argument   */
  193.    int       last_directory_char; /* last character in directory string    */
  194.    register  int  i;              /* Sub to use with attribute structure   */
  195.  
  196.    /*************************************************************************
  197.    *  Example:  The following argument will start on the A: drive.  The     *
  198.    *            BRIEF directory in the root will be searched and all files  *
  199.    *            and sub-directories in BRIEF will be displayed.             *
  200.    *                                                                        *
  201.    *     A:\BRIEF\*.*                                                       *
  202.    *     ^^      ^^                                                         *
  203.    *     ||      || *incoming_filename                                      *
  204.    *     ||      | *last_backslash                                          *
  205.    *     || *last_colon                                                     *
  206.    *     | *incoming_string                                                 *
  207.    *                                                                        *
  208.    *              *.*                                                       *
  209.    *              ^                                                         *
  210.    *              | check_string                                            *
  211.    *                                                                        *
  212.    *     A:\BRIEF\*.*                                                       *
  213.    *     ^        ^                                                         *
  214.    *     |        | last_directory_character (a subscript)                  *
  215.    *     |        | \0 (null)                                               *
  216.    *     | directory_string                                                 *
  217.    *************************************************************************/
  218.  
  219.    /*************************************************************************
  220.    *  Check number of incoming arguments and if incorrect, do error message *
  221.    *************************************************************************/
  222.  
  223.    if (argc < 2) {
  224.       printf ("Syntax is: WHERE [starting drive:\path]filename.ext [switches]\n");
  225.       printf ("           Switchs (in any order) mean:\n");
  226.       printf ("             p - stop and prompt at each file or directory\n");
  227.       printf ("             d - directories are prompted too\n");
  228.       printf ("             a - stop on modified files\n");
  229.       printf ("             s - stop on system files\n");
  230.       printf ("             h - stop on hidden files\n");
  231.       printf ("             r - stop on read only files\n");
  232.    }
  233.    else {
  234.       /*******************************************************************
  235.       *  Incoming_string is set to the first argument in the command line*
  236.       *  which should be the file spec.                                  *
  237.       *  The incoming_string is then searched for the last occurrence of *
  238.       *  a backslash to find the end of the directory name.              *
  239.       *  Argv points to the array of ptrs, each pointer pointing to a    *
  240.       *  separate command line argument.  *(argv+0) IS the pointer  to   *
  241.       *  the first argument (i.e. the program name WHERE).  *(argv+1) IS *
  242.       *  the pointer to the filespec.  *(argv+2) IS the pointer to the   *
  243.       *  first program switch                                            *
  244.       *******************************************************************/
  245.  
  246.       incoming_string = *(++argv);           
  247.  
  248.       if (argc >= 3) {
  249.          first_switch = *(++argv);  /* first_arg points at first switch */
  250.          first_switch_copy = first_switch;  /* need temp pointer        */
  251.          /* if p is a pointer, then strchr("?",p) wont't work, but      */
  252.          /*                         strchr("?",*p) will                 */
  253.          /* convert the first switch to lower case to simplify checking.*/
  254.          /* this second switch would not be needed if a function were   */
  255.          /* used to accomplish this conversion.                         */
  256.          for ( ; *first_switch_copy != '\0' ; first_switch_copy++ )
  257.             *first_switch_copy = tolower(*first_switch_copy);
  258.          if (strchr(first_switch,'p')) {
  259.             user_want_prompt = yes;
  260.          }
  261.          for (i = 0; i < SIZE(atr); i++) {
  262.             if (strchr(first_switch,atr[i].a_name)) {
  263.                attr_mask_switch |= atr[i].a_mask;
  264.             }
  265.          }
  266.       }
  267.       if (attr_mask_switch == 0x00) {
  268.          attr_mask_switch = 0xff;
  269.       }
  270.       last_backslash = strrchr(incoming_string,backslash);
  271.       last_colon     = strchr(incoming_string,colon);
  272.  
  273.       /*******************************************************************
  274.       *  If there was not a backslash but there was a colon, set the     *
  275.       *    backslash pointer to the colon pointer.                       *
  276.       *    (this reduces the number of further checks needed)            *
  277.       *                                                                  *
  278.       *  If there was not a backslash (and therefore the beginning       *
  279.       *     directory is the root directory)                             *
  280.       *     begin                                                        *
  281.       *        copy command line argument into check_string              *
  282.       *        copy root directory into directory_string                 *
  283.       *     end                                                          *
  284.       *  else                                                            *
  285.       *     (if there was a backslash and therefore a beginning          *
  286.       *      directory specified in the command line)                    *
  287.       *     begin                                                        *
  288.       *        set the incoming_filename to the next character           *
  289.       *           past the backslash                                     *
  290.       *        copy the incoming_filename into check_string              *
  291.       *        copy the command line argument into directory_string      *
  292.       *        terminate directory_string just after the last backslash  *
  293.       *           (therefore leaving only the directory name in the      *
  294.       *            string)                                               *
  295.       *     end                                                          *
  296.       *******************************************************************/
  297.  
  298.       if ((last_backslash == NULL) && (last_colon != NULL))
  299.          last_backslash = last_colon;
  300.  
  301.       if ((last_backslash == NULL) && (last_colon == NULL)) {
  302.          /* With no colon or backslash, argument is a filespec             */
  303.          strcpy(check_string,incoming_string);
  304.          strcpy(directory_string,"\\");
  305.       }
  306.       else {
  307.          if (last_backslash < last_colon) {
  308.             /* Here the colon followed the backslash                       */
  309.             printf("\nError: A colon was in the input after a backslash\n");
  310.             continue_pgm = stop;
  311.          }
  312.          else {
  313.             /* Here we had only backslash or colon, or backslash following */
  314.             /*   colon if both were present                                */
  315.             incoming_filename = last_backslash + 1;
  316.             strcpy(check_string,incoming_filename);
  317.             strcpy(directory_string,incoming_string);
  318.             last_directory_char = incoming_filename - incoming_string;
  319.             directory_string[last_directory_char] = end_of_string;
  320.             if (strlen(check_string) == 0)
  321.                strcpy(check_string,"*.*");
  322.          }
  323.       }
  324.  
  325.       /*******************************************************************
  326.       *  Start up the recursive functions                                *
  327.       *******************************************************************/
  328.  
  329.       if (continue_pgm == continue) {
  330.          printf ("\nStarting WHERE at \"%s\" looking for \"%s\"\n\n",directory_string,check_string);
  331.          if (user_want_prompt == yes) {
  332.             printf("  Valid responses: D - Delete  C - Change attribute  Q - Quit the program\n");
  333.             printf("                   N - Next directory                                    \n");
  334.             printf("                   Any other key continues to the next file\n\n");
  335.          }
  336.          LOOK(directory_string); /* directory_string ends in a backslash
  337.                                     or else is only a drive letter + colon */
  338.       }
  339.    }
  340.    return;
  341. }
  342.  
  343. /****************************************************************************
  344. *  LOOK is the recursive procedure in WHERE that is called once for each    *
  345. *     subdirectory that is found                                            *
  346. *  The argument (directory_string) is an array, and as such its reference   *
  347. *     as an argument causes a pointer to it (i.e. the array) to be          *
  348. *     generated and passed as the argument.                                 *
  349. *  The structure current_DTA is defined in this function, so while here,    *
  350. *     any references to it can be made 'directly', or by name without any   *
  351. *     an indirection operator.  But in functions called by this one, where  *
  352. *     the current_DTA is passed as an argument, references to the structure *
  353. *     must be made as (*current_DTA) or by whatever other name the DTA is   *
  354. *     referred.  See the GET_FILES function as an example.                  *
  355. ****************************************************************************/
  356.  
  357. LOOK(directory_string)
  358.  
  359. char *directory_string;
  360.  
  361. {
  362.    struct DTA current_DTA;       /* used to return data from DOS           */
  363.    filename newdirectory_string; /* the directory to be searched on the
  364.                                     next call to LOOK()                    */
  365.    filename current_string;      /* temporary filename string for      
  366.                                     searching the directories              */
  367.    filename chmod_dir_string;    /* temporary filename string for      
  368.                                     changing directory attribute           */
  369.    register  int  i;             /* Sub to use with attribute structure    */
  370.    char user_response;           /* holds keyboard response to prompt      */
  371.    char *last_dir_backslash;     /* ptr to last backslash in
  372.                                     chmod_dir_string                       */
  373.  
  374.    /*************************************************************************
  375.    *  Form current_string by copying directory_string and then              *
  376.    *     concatenating "*.*" to look through all files                      *
  377.    *  First we look through all directories, then when there are no more    *
  378.    *     directories the files are scanned.                                 *
  379.    *************************************************************************/
  380.  
  381.    strcpy(current_string,directory_string);
  382.    strcat(current_string,"*.*");
  383.  
  384.    /*************************************************************************
  385.    *  Set the Disk Transfer Area (DTA) in DOS to the current_DTA            *
  386.    *     structure                                                          *
  387.    *  Get the first subdirectory in this directory                          *
  388.    *************************************************************************/
  389.  
  390.    SET_DTA(¤t_DTA);
  391.  
  392.    GET_FIRST(current_string,directory_type);
  393.  
  394.    /*************************************************************************
  395.    *  While there are more subdirectories in this directory, and we are     *
  396.    *     to continue                                                        *
  397.    *     begin                                                              *
  398.    *        double check for proper directories (see text of article)       *
  399.    *        if a directory                                                  *
  400.    *           begin                                                        *
  401.    *              set up the newdirectory_string for the next call to       *
  402.    *                 LOOK (see text)                                        *
  403.    *              call LOOK                                                 *
  404.    *              reset Disk Transfer Address (see text of article)         *
  405.    *           end                                                          *
  406.    *        look for next directory                                         *
  407.    *     end                                                                *
  408.    *************************************************************************/
  409.  
  410.    while ((not(outregs.x.cflag))     &&
  411.           (continue_dir == continue) &&
  412.           (continue_pgm == continue)   ) {
  413.       if ( (current_DTA.DTA_attr & directory_only) &&
  414.            (current_DTA.DTA_filename[0] != '.'   )   ) {
  415.          strcpy(newdirectory_string,directory_string);
  416.          strcat(newdirectory_string,current_DTA.DTA_filename);
  417.          strcat(newdirectory_string,"\\");
  418.          if (strchr(first_switch,n_directory)) {
  419.             for (i = 0; i < SIZE(atr); i++) {
  420.                if (atr[i].a_mask & current_DTA.DTA_attr)
  421.                   putchar(atr[i].a_name);
  422.                else
  423.                   putchar('-');
  424.             }
  425.             printf("            %s  %s%s\n",
  426.                DATE(&(current_DTA.DTA_date)), directory_string,
  427.                current_DTA.DTA_filename);
  428.             if (user_want_prompt == no) {
  429.                ++nbr_lines_on_screen;
  430.                if (nbr_lines_on_screen >= full_screen) {
  431.                   nbr_lines_on_screen = 0;
  432.                   printf("Press any key to continue...\n");
  433.                   getch();
  434.                }
  435.             }
  436.             else {
  437.                user_response = getch();
  438.                if (strchr("Qq",user_response))
  439.                   continue_pgm = stop;
  440.                else {
  441.                   if (strchr("Nn",user_response))
  442.                      continue_dir = stop;  /*
  443.                   else {
  444.                      if (strchr("Cc",user_response)) {
  445.                         printf("Directory string is: %s\n",directory_string);
  446.                         strcpy(chmod_dir_string,directory_string);
  447.                         strcat(chmod_dir_string,current_DTA.DTA_filename);
  448.                         last_dir_backslash = strrchr(chmod_dir_string,backslash);
  449.                         *last_dir_backslash = end_of_string;
  450.                         CHANGE_FILE_ATTRIBUTE(chmod_dir_string,current_DTA.DTA_attr);
  451.                      }
  452.                   }  */
  453.                }
  454.             }
  455.          }
  456.          if (continue_pgm == continue) {
  457.             if (continue_dir == continue) {
  458.                LOOK(newdirectory_string);
  459.                SET_DTA(¤t_DTA);
  460.                continue_dir = continue;
  461.             }
  462.             else
  463.                continue_dir = continue;
  464.          }
  465.          else {
  466.             outregs.x.cflag = carry_set;
  467.             outregs.x.ax    = no_more_files;
  468.          }
  469.       }
  470.       GET_NEXT();
  471.    }
  472.  
  473.    /*************************************************************************
  474.    *  If there are no more subdirectories in this directory                 *
  475.    *     look for files                                                     *
  476.    *  else                                                                  *
  477.    *     print an error message                                             *
  478.    *************************************************************************/
  479.  
  480.       if (continue_pgm == continue) {
  481.          if (outregs.x.ax == no_more_files)
  482.             GET_FILES(directory_string,¤t_DTA);
  483.          else
  484.             printf("Problem with looking thru %s\n",directory_string);
  485.       }
  486.  
  487.    return;
  488. }
  489.  
  490. /****************************************************************************
  491. *  GET_FILES is called once per directory to look for the actual files      *
  492. *     the search string                                                     *
  493. ****************************************************************************/
  494.  
  495. GET_FILES(directory_string,current_DTA)
  496.  
  497. char *directory_string;
  498. struct DTA *current_DTA;
  499.  
  500. {
  501.    filename current_string;      /* temporary filename string for      
  502.                                     searching the directories              */
  503.    filename asciiz_file_name;    /* holds fully qualified asciiz name      */
  504.    char user_response;           /* holds keyboard response to prompt      */
  505.    char del_response;            /* holds keyboard response to del msg     */
  506.    register  int  i;             /* Sub to use with attribute structure    */
  507.    long nbr_bytes_this_dir = 0;  /* Sub to use with attribute structure    */
  508.  
  509.    /*************************************************************************
  510.    *  Form current_string by copying directory_string into it and then      *
  511.    *     concatenating the check_string onto the end                        *
  512.    *************************************************************************/
  513.  
  514.  
  515.    strcpy(current_string,directory_string);
  516.    strcat(current_string,check_string);
  517.    continue_dir = continue;
  518.  
  519.    /*************************************************************************
  520.    *  Get the first file that matches current_string                        *
  521.    *************************************************************************/
  522.  
  523.    GET_FIRST(current_string,no_type);
  524.  
  525.    /*************************************************************************
  526.    *  While there are more files that match the search string;              *
  527.    *     begin                                                              *
  528.    *        print the file information                                      *
  529.    *        handle prompting if requested                                   *
  530.    *        get the next file if not directed to stop                       *
  531.    *     end                                                                *
  532.    *************************************************************************/
  533.  
  534.    while ( (not(outregs.x.cflag))) {
  535.  
  536.       /* (*current_DTA).DTA_filename needs the parens because the . binds  */
  537.       /* more tightly than the * does.  It can also be written as          */
  538.       /* current_DTA -> DTA_filename, which says that 'current_DTA' is a   */
  539.       /* pointer to the structure in which DTA_filename is a member.       */
  540.  
  541.       /* Loop for each possible attribute bit, printing its symbol if the  */
  542.       /* bit is set, otherwise printing a '-'.                             */
  543.  
  544.       if ( (attr_mask_switch & (*current_DTA).DTA_attr) ||
  545.            (attr_mask_switch == 0xff)                   || 
  546.            (attr_mask_switch == a_directory) ) {
  547.          for (i = 0; i < SIZE(atr); i++) {
  548.             if (atr[i].a_mask & (*current_DTA).DTA_attr)
  549.                putchar(atr[i].a_name);
  550.             else
  551.                putchar('-');
  552.          }
  553.  
  554.          nbr_bytes_this_dir += (*current_DTA).DTA_size;
  555.          printf("%10ld  %s  %s%s\n", (*current_DTA).DTA_size,
  556.             DATE(&((*current_DTA).DTA_date)), directory_string,
  557.             (*current_DTA).DTA_filename);
  558.  
  559.          if (user_want_prompt == no) {
  560.             ++nbr_lines_on_screen;
  561.             if (nbr_lines_on_screen >= full_screen) {
  562.                nbr_lines_on_screen = 0;
  563.                printf("Press any key to continue...\n");
  564.                getch();
  565.             }
  566.          }
  567.          else {
  568.             user_response = getch();
  569.             if (strchr("Dd",user_response)) {
  570.                strcpy(asciiz_file_name,directory_string);
  571.                strcat(asciiz_file_name,(*current_DTA).DTA_filename);
  572.                printf("\n      Do you really want to delete %s (Y/N)?\n\n",asciiz_file_name);
  573.                del_response = getch();
  574.                if (strchr("Yy",del_response)) {
  575.                   DELETE_FILE(asciiz_file_name);
  576.                   if (not(outregs.x.cflag))
  577.                      printf("      %s  was DELETED\n\n",asciiz_file_name);
  578.                }
  579.             }
  580.             else {
  581.                if (strchr("Qq",user_response))
  582.                   continue_pgm = stop;
  583.                else {
  584.                   if (strchr("Nn",user_response))
  585.                      continue_dir = stop;
  586.                   else {
  587.                      if (strchr("Cc",user_response)) {
  588.                         strcpy(asciiz_file_name,directory_string);
  589.                         strcat(asciiz_file_name,(*current_DTA).DTA_filename);
  590.                         CHANGE_FILE_ATTRIBUTE(asciiz_file_name,(*current_DTA).DTA_attr);
  591.                      }
  592.                   }
  593.                }
  594.             }
  595.          }
  596.       }
  597.    
  598.       if ((continue_pgm == continue) &&
  599.           (continue_dir == continue)   )
  600.          GET_NEXT();
  601.       else {
  602.          outregs.x.cflag = carry_set;
  603.          outregs.x.ax    = no_more_files;
  604.       }
  605.    }
  606.  
  607.    /*************************************************************************
  608.    *  If error in looking for a file, print error message and return        *
  609.    *************************************************************************/
  610.  
  611.    if (outregs.x.ax != no_more_files)
  612.       printf("Problem with looking for %s\n",current_string);
  613.    else {
  614.       if (user_want_prompt == no) {
  615.          if (nbr_lines_on_screen >= full_screen) {
  616.             nbr_lines_on_screen = 0;
  617.             printf("Press any key to continue...\n");
  618.             getch();
  619.          }
  620.       }
  621.       if (strchr(first_switch,n_directory)) {
  622.          ++nbr_lines_on_screen;
  623.          printf("     %10ld  bytes found in directory %s\n",nbr_bytes_this_dir,directory_string);
  624.       }
  625.    }
  626.    return;
  627. }
  628.  
  629. /****************************************************************************
  630. *  GET_NEXT does an interrupt 21H, function 4FH                             *
  631. ****************************************************************************/
  632.  
  633. GET_NEXT()
  634.  
  635. {
  636.    inregs.x.ax = 0x4f00;
  637.    intdos(&inregs,&outregs);
  638.    return;
  639. }
  640.  
  641. /****************************************************************************
  642. *  SET_DTA does an interrupt 21H, function 1AH                              *
  643. *     The DS:DX pair is set to the addr of the current_DTA data structure   *
  644. *     The current_DTA comes in as an address.  Although a structure ref is  *
  645. *     not automatically converted to a pointer (as in the case of an array),*
  646. *     the caller sends the address by SET_DTA(&...).                        *
  647. ****************************************************************************/
  648.  
  649. SET_DTA(current_DTA)
  650.  
  651. struct DTA *current_DTA;
  652.  
  653. {
  654.    inregs.x.ax = 0x1a00;
  655.    inregs.x.dx = current_DTA;
  656.    intdos(&inregs,&outregs);
  657.    return;
  658. }
  659.  
  660. /****************************************************************************
  661. *  GET_FIRST does an intrrupt 21H, function 4EH                             *
  662. *     The CX register is set to either normal or directory type (see text)  *
  663. *     The DS:DX pair irs set to the address of the search string            *
  664. ****************************************************************************/
  665.  
  666. GET_FIRST(search_string,filetype)
  667.  
  668. char *search_string;
  669. int  filetype;
  670.  
  671. {
  672.    inregs.x.ax = 0x4e00;
  673.    inregs.x.cx = filetype;
  674.    inregs.x.dx = search_string;
  675.    intdos(&inregs,&outregs);
  676.    return;
  677. }
  678.  
  679. /****************************************************************************
  680. *  DELETE_FILE does a DOS interrupt 21H, function 41H                       *
  681. *     The DS:DX points to an ASCIIZ string specifying the file which is to  *
  682. *     be deleted.                                                           *
  683. ****************************************************************************/
  684.  
  685. DELETE_FILE(asciiz_file_name)
  686.  
  687. char *asciiz_file_name;
  688.  
  689. {
  690.    inregs.x.ax = 0x4100;
  691.    inregs.x.dx = asciiz_file_name;
  692.    intdos(&inregs,&outregs);
  693.    return;
  694. }
  695.  
  696. /****************************************************************************
  697. *  CHANGE_FILE_ATTRIBUTE does a DOS interrupt 21H, function 43H             *
  698. *     The DS:DX points to an ASCIIZ string specifying the file which is to  *
  699. *     have its attribute changed.  First though, the user is prompted for   *
  700. *     the new attribute to be used.                                         *
  701. *  Trying to change the attribute of a directory returns error code 5,      *
  702. *     access denied.                                                        *
  703. *  The (strchr(attr_response,???)) does not work with ??? equal to a DEFINE *
  704. *     value of '\v', '\a', '\n', or '\r'.  It does work with DEFINE         *
  705. *     values of '\s', '\u', '\h', and '\w'.  Changing them to literals      *
  706. *     solves the problem.                                                   *
  707. ****************************************************************************/
  708.  
  709. CHANGE_FILE_ATTRIBUTE(asciiz_file_name,current_attribute)
  710.  
  711. char *asciiz_file_name;
  712. char current_attribute;
  713.  
  714. {
  715.    char attr_response[7];           /* holds keyboard response to prompt   */
  716.    char *p_attr_response;           /* pointer to keyboard response        */
  717.  
  718.    printf("\n      Please enter up to 4 desired attribute settings:");
  719.    printf("\n        (S)ystem or (U)ser, (H)idden or (V)isible");
  720.    printf("\n        (R)ead only or (W)rite allowed, and");
  721.    printf("\n        (A)rchive bit set or (N)ot to be archived\n\n");
  722.  
  723.    attr_response[0] = 5;                   /* max nbr input characters,    */
  724.                                            /* including the CR             */
  725.    p_attr_response = cgets(attr_response); /* rtn ptr to start of chars    */
  726.                                            /* convert to lower case        */
  727.    for ( ; *p_attr_response != '\0' ; p_attr_response++ )
  728.       *p_attr_response = tolower(*p_attr_response);
  729.    inregs.x.cx = current_attribute;
  730.  
  731.    if (strchr(attr_response,n_ronly))
  732.       inregs.x.cx = inregs.x.cx | a_ronly;
  733.    if (strchr(attr_response,n_write))
  734.       inregs.x.cx = inregs.x.cx & 0x00FE;
  735.  
  736.    if (strchr(attr_response,n_hidden))
  737.       inregs.x.cx = inregs.x.cx | a_hidden;
  738.    if (strchr(attr_response,n_visible))
  739.       inregs.x.cx = inregs.x.cx & 0x00FD;
  740.  
  741.    if (strchr(attr_response,n_system))
  742.       inregs.x.cx = inregs.x.cx | a_system;
  743.    if (strchr(attr_response,n_user))
  744.       inregs.x.cx = inregs.x.cx & 0x00FB;
  745.  
  746.    if (strchr(attr_response,n_archive))
  747.       inregs.x.cx = inregs.x.cx | a_archive;
  748.    if (strchr(attr_response,n_not_archive))
  749.       inregs.x.cx = inregs.x.cx & 0x00DF;
  750.  
  751.    inregs.x.ax = 0x4301;
  752.    inregs.x.dx = asciiz_file_name;
  753.    intdos(&inregs,&outregs);
  754.    if (outregs.x.cflag) {
  755.       printf("Error code from CHMOD was %d\n",outregs.x.ax);
  756.    }
  757.    return;
  758. }
  759.  
  760. /****************************************************************************
  761. *  DATE takes the date field from the current DTA structure and returns a   *
  762. *     string containing the information in formatted ASCII                  *
  763. ****************************************************************************/
  764.  
  765. char *DATE(dateptr)
  766.  
  767. struct msdos_date *dateptr;
  768.  
  769. {
  770.    sprintf(datestring, "%02d-%02d-%2d  %02d:%02d  %s",
  771.       dateptr -> ms_month,  dateptr -> ms_day,
  772.       dateptr -> ms_year+80, (dateptr ->ms_hour)%12,
  773.       dateptr -> ms_min, time_of_day[((dateptr -> ms_hour)/12)]);
  774.    return(datestring);
  775. }
  776.